#ifndef __U2Frustum_H__
#define __U2Frustum_H__


#include "U2PreRequest.h"
#include "U2MemoryAllocatorConfig.h"
#include "U2StringUtil.h"
#include "U2Common.h"
#include "U2Matrix4.h"
#include "U2AxisAlignedBox3.h"
#include "U2Plane.h"
#include "U2Vector3.h"
#include "U2Vector2.h"
#include "U2VertexIndexData.h"
#include "U2RenderOperation.h"
#include "U2Quaternion.h"



U2EG_NAMESPACE_BEGIN


/** Specifies orientation mode.
*/
enum OrientationMode
{
    OR_DEGREE_0       = 0,
    OR_DEGREE_90      = 1,
    OR_DEGREE_180     = 2,
    OR_DEGREE_270     = 3,

    OR_PORTRAIT       = OR_DEGREE_0,
    OR_LANDSCAPERIGHT = OR_DEGREE_90,
    OR_LANDSCAPELEFT  = OR_DEGREE_270
};

/** Specifies perspective (realistic) or orthographic (architectural) projection.
*/
enum ProjectionType
{
    PT_ORTHOGRAPHIC,
    PT_PERSPECTIVE
};

/** Worldspace clipping planes.
*/
enum FrustumPlane
{
    FRUSTUM_PLANE_NEAR   = 0,
    FRUSTUM_PLANE_FAR    = 1,
    FRUSTUM_PLANE_LEFT   = 2,
    FRUSTUM_PLANE_RIGHT  = 3,
    FRUSTUM_PLANE_TOP    = 4,
    FRUSTUM_PLANE_BOTTOM = 5
};


class _U2Share U2Frustum : public MovableAlloc
{
protected:

    U2String                    mName;
    /// Frustum orientation mode
    mutable OrientationMode     mOrientationMode;
    /// Something's changed in the frustum shape?
    mutable bool                mRecalcFrustum;
    /// Something re the view pos has changed
    mutable bool                mRecalcView;
    /// Something re the frustum planes has changed
    mutable bool                mRecalcFrustumPlanes;
    /// Something re the world space corners has changed
    mutable bool                mRecalcWorldSpaceCorners;
    /// Something re the vertex data has changed
    mutable bool                mRecalcVertexData;
    /// Pre-calced projection matrix for the specific render system
    mutable U2Matrix4           mProjMatrixRS;
    /// Pre-calced standard projection matrix but with render system depth range
    mutable U2Matrix4           mProjMatrixRSDepth;
    /// Pre-calced standard projection matrix
    mutable U2Matrix4           mProjMatrix;
    /// Pre-calced view matrix
    mutable U2Matrix4           mViewMatrix;
    /// Orthographic or perspective?
    ProjectionType              mProjType;

    /// y-direction field-of-view (default 45)
    Radian                      mFOVy;
    /// Far clip distance - default 10000
    u2real                      mFarDist;
    /// Near clip distance - default 100
    u2real                      mNearDist;
    /// x/y viewport ratio - default 1.3333
    u2real                      mAspect;
    /// Ortho height size (world units)
    u2real                      mOrthoHeight;
    /// Off-axis frustum center offset - default (0.0, 0.0)
    U2Vector2                   mFrustumOffset;
    /// Focal length of frustum (for stereo rendering, defaults to 1.0)
    u2real                      mFocalLength;
    mutable U2AxisAlignedBox3   mBoundingBox;
    /// Have the frustum extents been manually set?
    bool                        mFrustumExtentsManuallySet;
    /// Frustum extents
    mutable u2real              mLeft, mRight, mTop, mBottom;
    /// The 6 main clipping planes
    mutable U2Plane             mFrustumPlanes[6];
    mutable U2Vector3           mWorldSpaceCorners[8];
    mutable U2VertexData        mVertexData;

    /// Stored versions of parent orientation / position
    mutable U2Quaternion        mLastParentOrientation;
    mutable U2Vector3           mLastParentPosition;

public:
    /// Small constant used to reduce far plane projection to avoid inaccuracies
    static const u2real         INFINITE_FAR_PLANE_ADJUST;

public:
    /// Named constructor
    U2Frustum(const U2String& name = U2StringUtil::BLANK);
    virtual ~U2Frustum();

    const U2String& getName(void) const { return mName; }

    /** Set the orientation mode of the frustum. Default is OR_DEGREE_0
    @remarks
        Setting the orientation of a frustum is only supported on
        iPhone at this time.  An exception is thrown on other platforms.
    */
    void setOrientationMode(OrientationMode orientationMode);
    /** Get the orientation mode of the frustum.
    @remarks
        Getting the orientation of a frustum is only supported on
        iPhone at this time.  An exception is thrown on other platforms.
    */
    OrientationMode getOrientationMode() const;

    /** Sets the aspect ratio for the frustum viewport.
    @remarks
        The ratio between the x and y dimensions of the rectangular area visible through the frustum
        is known as aspect ratio: aspect = width / height .
    @par
        The default for most fullscreen windows is 1.3333 - this is also assumed by Ogre unless you
        use this method to state otherwise.
    */
    virtual void setAspectRatio(u2real ratio);
    /** Retreives the current aspect ratio.
    */
    virtual u2real getAspectRatio(void) const;
	/** Manually set the extents of the frustum.
	@param left, right, top, bottom The position where the side clip planes intersect
		the near clip plane, in eye space
	*/
	virtual void setFrustumExtents(u2real left, u2real right, u2real top, u2real bottom);
	/** Reset the frustum extents to be automatically derived from other params. */
	virtual void resetFrustumExtents(); 
	/** Get the extents of the frustum in view space. */
	virtual void getFrustumExtents(u2real& outleft, u2real& outright, u2real& outtop, u2real& outbottom) const;
	/** Sets the orthographic window settings, for use with orthographic rendering only. 
	@note Calling this method will recalculate the aspect ratio, use 
		setOrthoWindowHeight or setOrthoWindowWidth alone if you wish to 
		preserve the aspect ratio but just fit one or other dimension to a 
		particular size.
	@param w, h The dimensions of the view window in world units
	*/
	virtual void setOrthoWindow(u2real w, u2real h);
	/** Sets the orthographic window height, for use with orthographic rendering only. 
	@note The width of the window will be calculated from the aspect ratio. 
	@param h The height of the view window in world units
	*/
	virtual void setOrthoWindowHeight(u2real h);
	/** Sets the orthographic window width, for use with orthographic rendering only. 
	@note The height of the window will be calculated from the aspect ratio. 
	@param w The width of the view window in world units
	*/
	virtual void setOrthoWindowWidth(u2real w);
	/** Gets the orthographic window height, for use with orthographic rendering only. 
	*/
	virtual u2real getOrthoWindowHeight() const;
	/** Gets the orthographic window width, for use with orthographic rendering only. 
	@note This is calculated from the orthographic height and the aspect ratio
	*/
	virtual u2real getOrthoWindowWidth() const;

    /** Gets the projection matrix for this frustum adjusted for the current
		rendersystem specifics (may be right or left-handed, depth range
		may vary).
    @remarks
        This method retrieves the rendering-API dependent version of the projection
        matrix. If you want a 'typical' projection matrix then use 
        getProjectionMatrix.

    */
    virtual const U2Matrix4& getProjectionMatrixRS(void) const;

    /** Overridden from Renderable */
    void getRenderOperation(U2RenderOperation& op);

    /** Gets the view matrix for this frustum. Mainly for use by OGRE internally.
    */
    virtual const U2Matrix4& getViewMatrix(void) const;

    /** Get the derived position of this frustum. */
    virtual const U2Vector3& getPositionForViewUpdate(void) const;
    /** Get the derived orientation of this frustum. */
    virtual const U2Quaternion& getOrientationForViewUpdate(void) const;

    static U2Matrix4 makeViewMatrix(const U2Vector3& position, const U2Quaternion& orientation, 
        const U2Matrix4* reflectMatrix = 0);

protected:
    // Internal functions for calcs
    virtual void calcProjectionParameters(u2real& left, u2real& right, u2real& bottom, u2real& top) const;
    /// Update frustum if out of date
    virtual void updateFrustum(void) const;
    /// Update view if out of date
    virtual void updateView(void) const;
    /// Implementation of updateFrustum (called if out of date)
    virtual void updateFrustumImpl(void) const;
    /// Implementation of updateView (called if out of date)
    virtual void updateViewImpl(void) const;
    virtual void updateFrustumPlanes(void) const;
    /// Implementation of updateFrustumPlanes (called if out of date)
    virtual void updateFrustumPlanesImpl(void) const;
    virtual void updateWorldSpaceCorners(void) const;
    /// Implementation of updateWorldSpaceCorners (called if out of date)
    virtual void updateWorldSpaceCornersImpl(void) const;
    virtual void updateVertexData(void) const;
    virtual bool isViewOutOfDate(void) const;
    virtual bool isFrustumOutOfDate(void) const;
    /// Signal to update frustum information.
    virtual void invalidateFrustum(void) const;
    /// Signal to update view information.
    virtual void invalidateView(void) const;
};


U2EG_NAMESPACE_END


#endif
